#include <config.h>
#include <core.h>

msgd_rbtree_t                 msgd_event_timer_rbtree;
static msgd_rbtree_node_t     msgd_event_timer_sentinel;
pthread_mutex_t               *msgd_event_timer_mutex; 

msgd_int_t    msgd_event_timer_init(void)
{

   msgd_rbtree_init(&msgd_event_timer_rbtree, &msgd_event_timer_sentinel,
                    msgd_rbtree_insert_timer_value);

   if(global.worker_threads_n > 0 ){
          
      if (msgd_event_timer_mutex) {
          return MSGD_OK;
      }   
  
      msgd_event_timer_mutex = msgd_mutex_init();
      if (msgd_event_timer_mutex == NULL) {
          return MSGD_ERROR;
      }
    
   }

   return MSGD_OK;
}

msgd_uint_t   msgd_event_find_timer(void)
{
    msgd_int_t      timer;
    msgd_rbtree_node_t  *node, *root, *sentinel;

    if (msgd_event_timer_rbtree.root == &msgd_event_timer_sentinel) {
        return MSGD_TIMER_INFINITE;
    }

    msgd_mutex_lock(msgd_event_timer_mutex);

    root = msgd_event_timer_rbtree.root;
    sentinel = msgd_event_timer_rbtree.sentinel;

    node = msgd_rbtree_min(root, sentinel);

    msgd_mutex_unlock(msgd_event_timer_mutex);

    timer = (msgd_int_t) node->key - (msgd_int_t) msgd_current_msec;

    return (msgd_uint_t) (timer > 0 ? timer : 0);
} 

void    msgd_expire_timers(void)
{
    msgd_event_t        *ev;
    msgd_rbtree_node_t  *node, *root, *sentinel;

    sentinel = msgd_event_timer_rbtree.sentinel;

    for ( ;; ) {

        msgd_mutex_lock(msgd_event_timer_mutex);

        root = msgd_event_timer_rbtree.root;

        if (root == sentinel) {
            msgd_mutex_unlock(msgd_event_timer_mutex);
            return;
        }

        node = msgd_rbtree_min(root, sentinel);

        /* node->key <= msgd_current_time */

        if ((msgd_int_t) node->key - (msgd_int_t) msgd_current_msec <= 0)
        {
            ev = (msgd_event_t *) ((char *) node - offsetof(msgd_event_t, timer));

            if (global.worker_threads_n > 0 && msgd_trylock(ev->lock) == 0) {

                /*
                 * We cannot change the timer of the event that is being
                 * handled by another thread.  And we cannot easy walk
                 * the rbtree to find next expired timer so we exit the loop.
                 * However, it should be a rare case when the event that is
                 * being handled has an expired timer.
                 */

                DEBUG("event %p is busy in expire timers", ev);
                break;
            }

            DEBUG("event timer del: %d: %d",msgd_event_ident(ev), ev->timer.key);

            msgd_rbtree_delete(&msgd_event_timer_rbtree, &ev->timer);

            msgd_mutex_unlock(msgd_event_timer_mutex);

            ev->timer.left = NULL;
            ev->timer.right = NULL;
            ev->timer.parent = NULL;

            ev->timer_set = 0;

            if (global.worker_threads_n > 0) {

                block_queue_push(&msgd_posted_events_queue,&ev->queue);

                msgd_unlock(ev->lock);
              
                //msgd_mutex_unlock(msgd_event_timer_mutex);

                continue;
            }

            ev->timedout = 1;

            ev->handler(ev);

            continue;
        }

        break;
    }

    msgd_mutex_unlock(msgd_event_timer_mutex);
}
